class HistoryFeature {
    canvas;
    historyUndo;
    historyRedo;
    extraProps;
    historyNextState;
    historyProcessing;

    constructor(canvas) {
        this.canvas = canvas;
        this.historyUndo = [];
        this.historyRedo = [];
        this.extraProps = ['selectable', 'editable'];
        this.historyNextState = this._historyNext();
        this.historyProcessing = false;

        this._historyInit();
    }

    _historyNext() {
        return JSON.stringify(this.canvas.toDatalessJSON(this.extraProps));
    }

    _historyEvents() {
        return {
            'object:added': this._historySaveAction.bind(this),
            'object:removed': this._historySaveAction.bind(this),
            'object:modified': this._historySaveAction.bind(this),
            'object:skewing': this._historySaveAction.bind(this),
        };
    }

    _historyInit() {
        this.canvas.on(this._historyEvents());
    }

    _historyDispose() {
        this.canvas.off(this._historyEvents());
    }

    _historySaveAction() {
        if (this.historyProcessing) return;

        const json = this.historyNextState;
        this.historyUndo.push(json);
        this.historyNextState = this._historyNext();
        this.canvas.fire('history:append', { json: json });
    }

    undo(callback) {
        this.historyProcessing = true;

        const history = this.historyUndo.pop();
        if (history) {
            this.historyRedo.push(this._historyNext());
            this.historyNextState = history;
            this._loadHistory(history, 'history:undo', callback);
        } else {
            this.historyProcessing = false;
        }
    }

    redo(callback) {
        this.historyProcessing = true;

        const history = this.historyRedo.pop();
        if (history) {
            this.historyUndo.push(this._historyNext());
            this.historyNextState = history;
            this._loadHistory(history, 'history:redo', callback);
        } else {
            this.historyProcessing = false;
        }
    }

    _loadHistory(history, event, callback) {
        this.canvas.loadFromJSON(history, () => {
            this.canvas.renderAll();
            this.canvas.fire(event);
            this.historyProcessing = false;

            if (callback) callback();
        });
    }

    clearHistory() {
        this.historyUndo = [];
        this.historyRedo = [];
        this.canvas.fire('history:clear');
    }

    onHistory() {
        this.historyProcessing = false;
        this._historySaveAction();
    }

    canUndo() {
        return this.historyUndo.length > 0;
    }

    canRedo() {
        return this.historyRedo.length > 0;
    }

    offHistory() {
        this.historyProcessing = true;
    }
}

export default HistoryFeature;
